/**
 * @overview This library is the core of Archetype Javascript Framework
 * @name ${project.name}
 * @author Temsa (Florian TRAVERSE) & Swiip (Matthieu LUX)
 * @version ${project.version}
 * @license
 * # Copyright (c) 2007-2008 Florian TRAVERSE and Matthieu LUX<br>
 * #<br>
 * # Permission is hereby granted, free of charge, to any person obtaining a copy<br>
 * # of this software and associated documentation files (the "Software"), to deal<br>
 * # in the Software without restriction, including without limitation the rights<br>
 * # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br>
 * # copies of the Software, and to permit persons to whom the Software is<br>
 * # furnished to do so, subject to the following conditions:<br>
 * #<br>
 * # The above copyright notice and this permission notice shall be included in<br>
 * # all copies or substantial portions of the Software.<br>
 * #<br>
 * # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
 * # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
 * # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br>
 * # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
 * # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br>
 * # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN<br>
 * # THE SOFTWARE.<br>
 */
 
/**
 * @namespace
 */
window._ = function() {};

var Archetype = {
    Version: '${project.version}',
    ConfigFile: "archetype.conf.js",

    Loggers:{},

    root: this,

    /**
     * List of parameters passed to the script when loaded
     */
    parameters:{},

    /**
     * Initialise the loaded module list
     */
    loadedModules: {},
    /**
     * Load the Framework.
     */
    load: function() {
    
        if(/webkit/i.test(navigator.userAgent)) {
            //TODO : remove the test on the useragent :/
            setTimeout(function() {
                if(document.readyState == "loaded" || document.readyState == "complete"){
                    Archetype._load();
                } else {
                    setTimeout(arguments.callee, 10);
                }
            }, 10);
        } else if((/mozilla/i.test(navigator.userAgent) && !/(compati)/.test(navigator.userAgent)) || (/opera/i.test(navigator.userAgent))) {
            //TODO : remove the test on the useragent :/
            document.addEventListener("DOMContentLoaded", Archetype._load, false);
        } else if(/*@cc_on!@*/false) {
            //This is used in IE browsers
            (function(){
                var t = document.createElement('doc:rdy');
                try {
                    t.doScroll('left');
                    Archetype._load();
                    t = null;
                } catch(e) {
                    setTimeout(arguments.callee, 0);
                }
            })();
        } else {
            //If we can't be ready before onload, then we just wait onload...
            window.onload = Archetype._load;
        }
    },
    /**
     * Load the Framework.
     * Set the path used to access Archetype in Archetype.path;
     * Should not be used by user.
     * @private
     */
    _load: function()
    {   
        //find his own include to get relative Path start
        var scripts=document.getElementsByTagName("script");

        if(typeof(scripts)!=='undefined')
        {
            for(i=0;i < scripts.length;i++)
            {
                var s=scripts[i];
                // get all parameters passed to the script itself
                if (s.src && s.src.match(/archetype\.js(\?.*)?$/)) {
                    Archetype.scriptElement = s;
                    Archetype.path = s.src.replace(/archetype\.js(\?.*)?$/, '');
                    // store options from the parameters passed to the script
                    var jsUrlParams = s.src.match(/archetype\.js\??(.*)?$/);

                    if (jsUrlParams.length == 2 && $exist(jsUrlParams[1])) {
                        var opts = jsUrlParams[1].split("&");
                        for (var j = 0; j < opts.length; j++) {
                            var opt = opts[j].split("=");
                            Archetype.parameters[opt[0]] = opt[1];
                        }
                    }
                    break;
                }
            }
        }
        else
            throw ("This is an unsupported browser :(");
        // This is mostly a fix for IE<8 (or IE8 in IE7 emulation mode) :
        // we check if we have a full or relative path
        // We need a full path if we want some functionnalities to work as intended
        if(! Archetype.path.match(/[^?#]+\:\/\/.+/)) {
            var bases = document.getElementsByTagName("base");
            if(bases.length > 0) {
                // handle special case of having a base tag in the header
                Archetype.path = bases[0].href + Archetype.path;
            } else {
                // find the last / without it being a
                Archetype.path = location.href.replace(/([^?#]+\/)(.*?)$/,'$1') + Archetype.path;
            }
        }
        //Initialise the configuration
        if(typeof(Archetype.libraryPath) == 'undefined') {
            Archetype.addJS(Archetype.path + Archetype.ConfigFile, Archetype._loadEngine);
        } else {
            Archetype._loadEngine();
        }

    },
    /**
     * Load what is necessary for the application boot
     * @param {String} url
     * @param {Function} callback
     */
    _prepareBoot: function(/*String* url, *Function* callback*/) {

     
        if(typeof Archetype.parameters.load != "undefined")
            Archetype.addJS(Archetype.parameters.load, Archetype._onEnvironmentLoaded);
        else if(typeof Archetype.parameters.component != "undefined")
            Archetype.requireComponent(Archetype.parameters.component, Archetype._onEnvironmentLoaded)
        else {
            url = /*url ||*/ location.href;
            var regexp,result,pageName;

            if(typeof Archetype.pages.rootEquivalence == "undefined")
                Archetype.pages.rootEquivalence=Archetype.path;

            if(!$exist(Archetype.parameters.page)) {
                regexp = new RegExp("^.*" + Archetype.pages.rootEquivalence + "([a-zA-Z0-9%/]*)\.?.*$","");
                result = regexp.exec(url);
                if(!result || result.length < 2) {
                    throw("Archetype.pages.rootEquivalence \"" + Archetype.pages.rootEquivalence + "\" has not been found in the URL " + url + ". Please correct the configuration.");
                }

                pageName=result[1];
                if(pageName === "") {
                    pageName = Archetype.pages.defaultPage;
                }
            } else {
                pageName = Archetype.parameters.page
            }
            Archetype.pages.pageName = pageName;
            Archetype.pages.urlPage = Archetype.path + Archetype.pages.relativePathToPages + pageName + ".js";
        
            Archetype.addJS(Archetype.pages.urlPage, Archetype._postPageLoad);
        }
    },
    /**
     * Checks that the page controller is ok
     * @private
     */
    _postPageLoad: function() {
        if(typeof this[Archetype.pages.pageName] == "undefined") {
            throw ("Can't find the object \"" + Archetype.pages.pageName + "\" which should have been added by \"" + Archetype.pages.urlPage + "\"");
        }
        if(typeof this[Archetype.pages.pageName].main == "undefined") {
            throw ("Can't find the function \"" + Archetype.pages.pageName + ".main()\" which should have been added by \"" + Archetype.pages.urlPage + "\"");
        }
        Archetype._onEnvironmentLoaded();
    },
    /**
     * Check launch page validity and load engine
     * @private
     */
    _loadEngine: function() {
  		
        //let's add a shortcut to an emptyFunction
        this._ = function() {}

        //Initialize loaderQueue
        Archetype.loaderQueue = {
            queue: [],
            currents: [],
            callbacks: []
        };
    
        Archetype._loadImplementation("engine", Archetype._loadArchetypeClass);
    },
    /**
	 * Load Archetype Class improvements.
	 * @private
	 */ 
    _loadArchetypeClass: function() {
        if(Archetype.useArchetypeClass === true) {
            Archetype.addJS($U('Archetype.Class'), Archetype._loadLogger);
        } else {
            Archetype._loadLogger();
        }
    },
    /**
     * Initialize Class definition, initialize loader queue and load logger
     * @private
     */
    _loadLogger: function() {
        //We override the Class definition
        if (typeof Class!= 'undefined' && typeof Archetype.Class!= 'undefined'
            && typeof Archetype.classOverride!= 'undefined' && typeof Archetype.classOverride!= false){
            Object.extend(Class, Archetype.Class);
        }
    	
        //Time to load the logger
        Archetype._loadImplementation("logger", Archetype._loadTemplate);
    },
    /**
     * Initialize global Logger object and load template
     * @private
     */
    _loadTemplate: function() {
        //Set the global Logger object in order to use logger easily
        Logger = Archetype.Logger;
        //Time to load the template implementation

        Archetype._loadImplementation("template", Archetype._loadDefaultModules);
    },
    /**
     * Load default modules of the configuration
     * @private
     */
    _loadDefaultModules: function() {
        Archetype.require(Archetype.defaultModules, {
            callback: Archetype._prepareBoot
        });
    },
    /**
     * Fired when the whole environnement is available
     * @private
     */
    _onEnvironmentLoaded: function() {
        if(Archetype.useEvents ===true) {
            Archetype.engine.fire("Environment", "Loaded");
        }

        Logger.log("Archetype Environnement is available :)");
        if(typeof Archetype.parameters.callback != "undefined")
            //launch the callback (the file should have been loaded using the load parameter)
            Archetype.root[Archetype.parameters.startCallBack]();
        else if (typeof Archetype.parameters.component != "undefined")
            //Instanciate the root component for the application
            new (Archetype.Component.get(Archetype.parameters.component))();
        else
            //Launch main of the launch page
            Archetype.root[Archetype.pages.pageName].main();
    },
    
    /**
     * We've got several times the same schema for a functionnality :
     * - one abstract interface
     * - one implementation (or a wrapper to an implementation)
     * - 0, one or more libs for the implementation
     * This function load all files for the functionnality in the right order.
     * @private
     */
    _loadImplementation: function(type, callback) {
    	 
        var typeWithUpperCase = type.substr(0, 1).toUpperCase() + type.substr(1);
        var name = (Archetype.parameters[type] || (typeof Archetype[type] == "string" ? Archetype[type] : Archetype[type].name));
        var nameWithUpperCase = name.substr(0, 1).toUpperCase() + name.substr(1);
        var abstractUrl = $U("Archetype." + type + ".abstract" + typeWithUpperCase);
        var wrapperModule = "Archetype." + type + "." + name + typeWithUpperCase;
        var wrapperUrl = $U(wrapperModule);
        var needs = [];

        if(typeof Archetype.modules[wrapperModule] != "undefined" && 
            typeof Archetype.modules[wrapperModule].needs != "undefined")
            needs = Archetype.modules[wrapperModule].needs;
    	
        if(Archetype.debugLoadImplementation) {
            console.debug("_loadImplementation type=", type, " abstractUrl=", abstractUrl);
        }
        if(! Archetype.isLoadedModule(abstractUrl)) {
            Archetype.addJS(abstractUrl, function() {
                var load = function() {
                    if(Archetype.debugLoadImplementation) {
                        console.debug("_loadImplementation.load wrapperUrl=", wrapperUrl, " needs=", needs);
                    }
                    if(needs.length > 0) {
                        var url = $U(needs.shift());
                        if(! Archetype.isLoadedModule(url)) {
                            Archetype.addJS(url, load);
                        }
                    } else {
                        if(! Archetype.isLoadedModule(wrapperUrl)) {
                            Archetype.addJS(wrapperUrl, function() {
                                if(typeof Archetype[typeWithUpperCase + "s"] == "undefined") {
                                    Archetype[typeWithUpperCase + "s"] = {};
                                }
                                Archetype[typeWithUpperCase] = Archetype[typeWithUpperCase + "s"][nameWithUpperCase + typeWithUpperCase];
                                callback();
                            });
                        }
                    }
                };
                load();
            });
        }
    },
    /**
     * Add a script file to the header.
     * Should not be used by user.
     * @param {String} file
     * @param {Function} onloadFunc
     * @param {Boolean} [defer]
     */
    addJS: function(/*String*/ file, /*Function*/ onloadFunc, /*Boolean*/ defer) {
        if (!onloadFunc) {
            onloadFunc = _;
        }
        //Short way out for files already added
        if(Archetype.loadedModules[file]) {
            onloadFunc();
            return;
        }
        //should be defined when configuration has been read and when
        //the corresponding logger file has been loaded
        if(typeof Logger != 'undefined') {
            Logger.log("Adding JS: " + file);
        }
        Archetype.loadedModules[file] = true;
        
        //creating the script dom element
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = file;
        script.charset = Archetype.charset||'iso-8859-1';
        if(typeof defer == "undefined" || defer) {
            script.defer = true;
        }
        script.startLoadingTime = new Date();

        /**
         * Callback to know that the JavaScript has been loaded
         * @ignore
         */
        script.onload = function() {
            if (script.readyState && script.readyState != "loaded" && script.readyState != "complete") {
                return;
            }
            script.onreadystatechange = script.onload = null;
            script.loaded = true;
            var loadingTime = (new Date()).getTime() - script.startLoadingTime;
            if($exist(Archetype.maxStaticLoadingTime)) {
                if(loadingTime > Archetype.maxStaticLoadingTime) {
                    Archetype.maxStaticLoadingTime = loadingTime;
                }
            } else if(loadingTime > 200) {
                Archetype.maxStaticLoadingTime = loadingTime;
            }

            if(Archetype.modules) { //Configuration ready
                Archetype._provide(file);
            }
            onloadFunc();
        };

        if(navigator.appName != "Opera") {
            script.onreadystatechange = script.onload;
        }

        if($exist(Archetype.maxStaticLoadingTime)) {
            window.setTimeout(function() {
                if(script.loaded != true) {
                    throw ("The script \"" + file + "\" has not been found.");
                }
            }, Archetype.maxStaticLoadingTime * 2);
        }

        Archetype.scriptElement.parentNode.appendChild(script);
    },
    /**
     * Add a CSS in the header.
     * @param {String} file real path
     */
    addCSS: function(/*String*/file)
    {
        var link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href=file;

        var head = document.getElementsByTagName("head")[0];
        head.appendChild(link);
        //ToDo: check that there's no problem with Safari 2.0 ...
        if(Archetype.modules) { //Configuration ready
            Archetype._provide(file);
        }
    },
    /**
     * Check if the module is already loaded
     * @param {String} module
     */
    isLoadedModule: function(/*String*/ module) {
        var realPath = $U(module);
        return typeof Archetype.loadedModules[realPath] != "undefined" && Archetype.loadedModules[realPath];
    },
    /**
     * Analyse the module name by checking if it is already loaded, mark it as being loaded if is not and return the real path
     * @param {String} module name to check
     * @return {Boolean} false if the require has to be done, true if not
     * @private
     */
    _startLoadingModule: function(/*String*/ module) {
        if(Archetype.isLoadedModule(module) && Archetype.getTypeForRealPath(module) != "html") { //In special case of require an html file, we need the content. So we emule the need to get it for getting it with browser cache
            return true; //Mean that the module was already loaded
        }
        Archetype.loadedModules[module] = false; //Mean that the module is currently loading
        return false; //Mean that the module has to be loaded
    },

    /**
     * Used to register modules.
     * Should not be used by user.
     * @param {String} module
     * @private
     */
    _provide: function(/*String*/ module) {
        var url = $U(module);
        if(! Archetype.isLoadedModule(url)) {
            Archetype.loadedModules[url] = true;
            return false; //Mean that the module wasn't loaded
        } else {
            return true; //Mean that the module was already loded
        }
    },

    /**
     * Get a list of module string parts
     * @param {String} module : module to split
     * @return {Array<String>} module and its parents modules (aka module hierarchy)
     */
    _getModuleHierarchy: function(/*String*/module) {
        var hierarchy=[];
        for(var index = module.indexOf("."); index > -1 && module.substr(0, index) != ""; index = module.indexOf(".", index + 1 ))
        {
            hierarchy.push(module.substr(0, index));
        }
        hierarchy.push(module);
        return hierarchy;
    },

    /**
     *
     */
    _getModuleUrlFromConfiguration: function(/*String*/module, /*Optionnal String*/tagReplaced) {
        var originalModule = module;
        var moduleHierarchy = Archetype._getModuleHierarchy(module);
		
        //replace the first occurence of a configuration in the hierarchy (starting from the most detailled one)
        for ( var i = moduleHierarchy.length - 1 ; i>=0 ; i--)
        {
            var tag = moduleHierarchy[i];
            //checks if this has not been already replaced one time, occurs when a rPath redirect to a subdirectory
            var hasNotAlreadyBeenReplaced = typeof tagReplaced != "undefined" ? tagReplaced.indexOf(tag)!=0 : true;
            if(tag !== "" && hasNotAlreadyBeenReplaced && typeof Archetype.modules[tag] != "undefined") {
                if(typeof Archetype.modules[tag].rPath != "undefined"
                    && Archetype.modules[tag].rPath != module) {
                    module = module.replace(tag, Archetype.modules[tag].rPath);
                    if(originalModule != module)
                        module = Archetype._getModuleUrlFromConfiguration(module, Archetype.modules[tag].rPath);
                    break;
                }
                else if (typeof Archetype.modules[tag].aPath != "undefined"
                    && Archetype.modules[tag].aPath != module) {
                    module = module.replace(tag, Archetype.modules[tag].aPath);
                    if(originalModule != module)
                        module = Archetype._getModuleUrlFromConfiguration(module, Archetype.modules[tag].rPath);
                    break;
                }
            }
        }
        return module;
    },

    /**
     * Return the URL for the module string in parameter.
     * If the module parameter is already an URL, just return it.
     * @param {String} module module name or an URL
     * @param {String} evaluation Evaluation tag for interpretation of the module
     */
    getUrl: function(/*String*/ module, /*String*/ evaluation) {
        if(module.indexOf("://") != -1) {
            //"://" indicates that there is the protocol in the module name and in this case this is already a real path
            return module;
        }
        if (Archetype.compiler)
            window.alert(module);
        
        module = Archetype._getModuleUrlFromConfiguration(module);

        //delete any path in common with archetype
        if(module.indexOf(Archetype.path) == 0) {
            module = module.substring(Archetype.path.length);
        }

        evaluation = (typeof evaluation == "string") ? evaluation : "js"; // default evaluation is "js"
        var type = (evaluation == "component") ? "js" : evaluation; // default type is "js"
        var extension = false;

        //get the type from the extension if available
        if(/\./.test(module))
        {
            type = Archetype.getTypeForRealPath(module)||type;//keep current type if undefined in module name
            var lastPartRegExp = /.*\.(.*?)$/;//last non escaped "." in an expression
            if( lastPartRegExp.test(module) )
            {
                var lastPart = lastPartRegExp.exec(module)[1];
                // extension is lastPart or default, we check if lastPart is a valid extension
                extension =  (Archetype.extensions[type].toString().indexOf(lastPart) >= 0) ? lastPart : false;
            }
        }
        if(! extension) {
            module += "." + ((Archetype.defaultExtensions && Archetype.defaultExtensions[type]) ? Archetype.defaultExtensions[type] : type);
        }
        var isAbsolute = ( module.indexOf("://") > -1 );//checks if module contains a protcol definition to know if a absolute path was used
        var ret = ((!isAbsolute)? Archetype.path : "") + module.replace(/\./g, "/").replace(/#/g, ".").replace(/(.*[^\\])\//g, "$1.").replace(/\/\//g,"/").replace(/^(file:\/|http:)/g,"$1/");
    
        /* If we're in compilation mode */
        if (Archetype.compiler)
            window.alert(ret);
			
        return ret    
    },

    getModuleFromUrl: function(/*String url*/ url) {
        var module = url.replace(Archetype.path, "");
        
        module = module.replace(/(.*)\..*?$/, "$1");
        module = module.replace(/\./g, "#");
        module = module.replace(/\//g, ".");
        
        return module;
    },

    /**
     * Find the type (html, js or css) of the real path in parameter.
     * @param {String} realPath real path of the file of which we want find the type
     */
    getTypeForRealPath: function(/*String*/ realPath) {
        if(/\./.test(realPath)) { //If there is a "."
            var ext = /.*\.(.*)$/.exec(realPath)[1]; //End of string after the last "."
            var types = [Archetype.defaultExtensions.js||"js", Archetype.defaultExtensions.css||"css", Archetype.defaultExtensions.html||"html"];

            for(var i = 0; i < types.length; i++) {
                var extensions = Archetype.extensions[types[i]];
                for(var j = 0; j < extensions.length; j++) {
                    if(extensions[j] == ext) {
                        return types[i];
                    }
                }
            }
        }
        else {
            throw "Can't find Type for realPath : " + realPath;
        }
        return null;
    },

    /**
     * Creates an absolute namespace for storing the values
     * Absolute indicates that namespace in parameter start in Archetype.root
     *
     * @example
     *    $N("foo.bar",{barfoo:5});
     *    -> Archetype.root.foo.bar.barfoo == 5
     * @param {String} namespace
     * @param {baseType or Array or HashMap} values...
     * @see $N
     */
    absoluteNamespace: function(/*String namespace, baseType or Array or HashMap values ...*/) {
        var args = [];
        for (var i = 0, length = arguments.length; i < length; i++) {
            args.push(arguments[i]);
        }
        var namespace = args.shift();
        var values = args;
        Archetype._namespace(true, namespace, values);
    },
    /**
     * Creates a relative namespace for storing the values
     * Relative indicates that an HashMap with keys is returned
     * @example
     *    $$N("foo.bar", {foobar:5}) == {foo: {bar: {foobar: 5}}}
     * @param {String} namespace
     * @param {baseType or Array or HashMap} values...
     * @see $$N
     */
    relativeNamespace: function(/*String namespace, baseType or Array or HashMap values ...*/) {
        var args = [];
        for (var i = 0, length = arguments.length; i < length; i++) {
            args.push(arguments[i]);
        }
        var namespace = args.shift();
        var values = args;
        return Archetype._namespace(false, namespace, values);
    },
    /**
     * Generic namespace for relative and absolute cases
     * @private
     */
    _namespace: function(/*Boolean*/ absolute, /*String*/ namespace, /*HashMap []*/ valuesList) {
        var keys = namespace.split(".");
        //root is a local HashMap for relative and Archetype.root (window by default) for absolute
        var root = {};
        if(absolute) {
            root = Archetype.root;
        }
        //assure that each key of the namespace is created
        var lastCurrent = root;
        var current = root;
        for(var i = 0; i < keys.length; i++) {
            if(typeof current != "object" || typeof current[keys[i]] == "undefined") {
                current[keys[i]] = {};
            }
            lastCurrent = current;
            current = current[keys[i]];
        }
        //populates values in parameters in the namespace initialized
        for (var i = 0; i < valuesList.length; i++) {
            var values = valuesList[i];
            if(typeof values == "object" && typeof values.length == "undefined") {
                //case of an HashMap, put all keys in the namespace
                for (var property in values) {
                    current[property] = values[property];
                }
            } else {
                //cas of a baseType or an array, we put the value in the namespace
                lastCurrent[keys[keys.length - 1]] = values;
            }
        }
        return root;
    },

    /**
     * /!\Can't be used before the bootstrap.
     * Recursive method for getting all subModules in the configuration.
     * @param {String} prefix
     * @param {String} module
     * @private
     */
    _recurseOnSubModule: function(/*String*/ prefix, /*String*/ module) {
        if(typeof module == "string") {
            if(prefix !== "") {
                module = prefix + "." + module;
            }
            if(typeof Archetype.modules[module] != "undefined" && typeof Archetype.modules[module].subModules != "undefined") {
                return this._recurseOnSubModule(module, Archetype.modules[module].subModules);
            } else {
                return [module];
            }
        } else { //typeof = object -> array
            var array = [];
            module.each(function(item) {
                var recurse = this._recurseOnSubModule(prefix, item);
                array = array.concat(recurse);
            }.bind(this));
            return array;
        }
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Recursivly add and complete needed layers for loading modules
     * Manage needs and subModules configuration
     * @param {String or Array} module module or modules to load
     * @param {Array} layers already prepared
     * @private
     */
    _addDependencies: function(/*String or Array*/ module, /*Array*/ layers) {
        var modules = null;
        //Define if there is configured modules on the list
        if(typeof module == "string") {
            modules = $A([module]);
        } else {
            modules = $A(module);
        }
        //Logger.warn("modules=", modules, " -- layers=", layers);
        
        var definedModules = $A();
        var notDefinedModules = $A();
        modules.each(function(module) {
            if(typeof Archetype.modules[module] != "undefined") {
                definedModules.push(module);
            } else {
                notDefinedModules.push(module);
            }
        });
        
        //Logger.warn("defined=" + definedModules.inspect() + " -- notDefined=" + notDefinedModules.inspect());
        if(definedModules.length > 0) {
            modules = notDefinedModules;

            //Recursion on the module declaration for searching possibles subModules configured
            var gotSubModules = false;
            definedModules.each(function(module) {
                if(typeof Archetype.modules[module].subModules != "undefined") {
                    //TODO copy rPath and aPath information to submodules -> add a pathInherit option ?
                    var subModules = Archetype._recurseOnSubModule("", module);
                    modules = modules.concat(subModules);
                    subModules.each(function(module) {
                        if(typeof Archetype.modules[module] != "undefined") {
                            definedModules.push(module);
                        }
                    });
                    gotSubModules = true;
                } else {
                    modules = modules.concat(module);
                }
            });
            //Logger.warn("withSubModules=" + modules.inspect() + " -- definedModules=" + definedModules.inspect());

            //Loading dependencies
            var needs = $A();
            var needsToLoad = $A();
            definedModules.each(function(module) {
                if(typeof Archetype.modules[module].needs != "undefined") {
                    needs = needs.concat(Archetype.modules[module].needs);
                }
            });

            needs.each(function(module) {
                //Copy of the Protoype's include core method to be sure using a simple equality
                var found = false;
                needsToLoad.each(function(value) {
                    if (value == module) {
                        found = true;
                        throw $break;
                    }
                });
                if(! Archetype.isLoadedModule(module) && ! found) {
                    needsToLoad.push(module);
                }
            });

            if(needsToLoad.length > 0) {
                return Archetype._addDependencies(needsToLoad, $A([modules]).concat(layers));
            }
        }
        return $A([modules]).concat(layers);
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Convert all modules names by his real path.
     * @param {Array} layers layers of a require
     * @param {String} evaluation
     * @private
     */
    _layersToRealPaths: function(/*Array*/ layers, /*String*/ evaluation) {
        //Logger.warn("layersToRealPaths 1 ", layers);
        layers = $A(layers).collect(function(layer) {
            //Logger.warn("layersToRealPaths 2 ", layer);
            layer = $A(layer).collect(function(module) {
                //Logger.warn("layersToRealPaths 3 ", module);
                return $U(module, evaluation);
            });
            return layer;
        });
        return layers;
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Remove duplicates urls in order to keep only the one in the higher layer.
     * @param {Array} layers layers of a require
     * @private
     */
    _withoutDuplicates: function(layers) {
        for(var i = 0; i < layers.length; i++) {
            var excludes = $A();
            for(var j = 0; j < i; j++) {
                excludes = excludes.concat(layers[j]);
            }
            layers[i] = layers[i].reject(function(module) {
                //Copy of the Protoype's include core method to be sure using a simple equality
                var found = false;
                excludes.each(function(value) {
                    if (value == module) {
                        found = true;
                        throw $break;
                    }
                });
                return found;
            });
        }
        return layers;
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Resolve all files to load to complete the require.
     * Manage layers of loading and module to real path conversion
     * @param {String or Array} module module's name or array of modules names
     * @param {String} evaluation evaluation specified in the require
     * @private
     */
    _resolve: function(/*String or Array*/ module, /*String*/ evaluation) {
        if(Archetype.debugResolver) {
            Logger.warn("Start resolve, modules=", [module], ", evaluation=", evaluation);
        }
        var layers = Archetype._addDependencies(module, []);
        if(Archetype.debugResolver) {
            Logger.warn("After addDependencies, layers=", layers);
        }
        layers = Archetype._layersToRealPaths(layers, evaluation);
        if(Archetype.debugResolver) {
            Logger.warn("After layersToRealPaths, layers=", layers);
        }
        layers = Archetype._withoutDuplicates(layers);
        if(Archetype.debugResolver) {
            Logger.warn("After withoutDuplicates, layers=", layers);
        }
        return layers;
    },

    /**
     * /!\Can't be used before the bootstrap.
     * Tells archetype that you need a particular module.
     * @param {String or Array} module : module's name or url
     * @param {Hashmap} options options as follow :
     * 	      {Boolean} evaluation by default, eval is evaluated by the file extension of the module (js = true, other = false)
     *                             true, url loaded is evaluated
     *        {Function} callback callback used when in asynchronous mode
     *        {HashMap} storing indicate an Hashmap for storing XHR results.
     */
    require: function(/*String or Array*/ module, /*HashMap*/ options) {
        //by default, this is a simple JS, so should be evaluated:
        //TODO: get a method in order to evaluate what default option should be used depending of the file type
        options = options || {
            evaluation: true,
            asynchronous: true
        };
        if(options.evaluation == "component") {
            Archetype._requireComponent(module, options);
        } else {
            var layers = Archetype._resolve(module, options.evaluation)
            if(Archetype.debugLoadLayers === true)
                Logger.debug("layers resolved", layers);
            Archetype._recurseOnRequire(layers, options, {});
        }
    },
    
    _requireComponent: function(/*String or Array*/ module, /*HashMap*/ options) {
        if(typeof module != "string") {
            var modules = $A(module);
        } else {
            var modules = $A();
            modules.push(module);
        }
    	
        var componentsToLoad = $A();
        modules.each(function(module) {
            if(!Archetype.Components.get(module)) {
                componentsToLoad.push(module);
            }
        });
    	
        new Archetype.LoadJoiner("Component", componentsToLoad, function() {
            if(componentsToLoad.length == 1) {
                return options.callback(Archetype.Components.get(componentsToLoad[0]));
            } else {
                var components = $A();
                componentsToLoad.each(function(item) {
                    components.push(Archetype.Components.get(item));
                });
                return options.callback(components);
            }
        });
        
        componentsToLoad.each(function(module) {
            var url = $U(module).toString();
            //If the component file has already be launched for loading
            //LoadJoiner will fire when component will be ready, no need to require it again
            //If we authorized the require, beware to remove the remove the callback, we cant resolve a component two times
            if(typeof Archetype.loadedModules[url] == "undefined") {
                //Logger.warn("require js file ", url, "\n for component ", module);
                Archetype.require(url, {
                    evaluation: "js",
                    callback: function() {
                        //Logger.warn("required js file ", url, "\n for component ", module);
                        Archetype.Component._resolveComponent(url);
                    }.bind(this)
                });
            }
            else {
                Archetype.Component._resolveComponent(url);
            }
        });
    },

    _recurseOnRequire: function(/*Array*/ layers, /*HashMap*/ options, /*HashMap*/ callbackData) {
        if(layers.length > 0) {
            var newOptions = Object.clone(options);
            newOptions.callback = function(newCallbackData) {
                callbackData = Object.extend(callbackData, newCallbackData);
                Archetype._recurseOnRequire(layers.slice(1), options, callbackData);
            }
            Archetype._layerRequire(layers[0], newOptions);
        } else {
            if(typeof options.callback == "function") {
                options.callback(callbackData);
            }
        }
    },

    _layerRequire: function(/*Array*/ modules, /*HashMap*/ options) {
        if(Archetype.debugLoadLayers === true)
            Logger.debug("layer require ", modules);

        //Initialize options with defaults values if necessary
        options = Object.extend({
            evaluation: true,
            asynchronous: true,
            callback: _,
            storing: false
        }, options || {});

        modules = $A(modules);
        var modulesToLoad = $A();
        allLoaded = true;
        modules.each(function(module) {
            var alreadyLoaded = this._startLoadingModule($U(module, options.evaluation));
            if(! alreadyLoaded) {
                allLoaded = false;
                modulesToLoad.push(module);
            }
        }.bind(this));
        modules = modulesToLoad;
        //Logger.warn(modules.inspect());

        if(allLoaded) {
            options.callback();
        } else {
            Archetype._loader(modules, options, null);
        }
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Archetype.require alias to load easily a component asynchronously
     *
     * @param {String} module component module name
     * @param {Function or Arguments} [callback] function to call when component is loaded.
     *     If callback is undefined, this will instanciate a new component with default constructor,
     *     (if you put anything else) as callback
     * @throws {String} Exception if no module has been defined
     * @example Archetype.requireComponent("my.components.first");
     * todo : example Archetype.requireComponent("my.components.first", 1, "hello, world", ["foo","bar"]);
     * @example Archetype.requireComponent("my.components.first", function(mycomponent){ alert("hello!");});
     *
     */
    requireComponent: function (/*String*/module, /*Function*/callback ) {
        if (typeof module.length != "undefined" && module.length == 0)
            return ;
        if (typeof module != "string" && url == null)
            throw "Invalid module in Archetype.requireComponent";

        var _callback = callback;
        if (typeof _callback != "function") {
            _callback = function(component){
                Logger.debug("Default requireComponent callback instanciating ", component);
                return ( new component() );
            }
        }
        /** @ignore */
        var mycallback = function() {
            var component = Archetype.Components.get(module);
            return ( _callback(component) );
        };
        
        if (Archetype.Component && Archetype.Component.get(module) != null) {
            return mycallback();
        }

        Archetype.require(module, {
            evaluation: "component",
            callback: mycallback
        });
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Sequencer for all requires. Used for add modules and as callback when modules are finished.
     * @param {String []} modules
     * @param {Hashmap} options
     * @param {String} finishedModule
     * @param {HashMap} callbackData
     * @private
     */
    _loader: function(/*String []*/ modules, /*Hashmap*/ options, /*String*/ finishedModule, /*HashMap*/ callbackData) {
        if(Archetype.debugLoader === true)
            Logger.debug("LOADER modules : ", modules, ", options : ", options, ", finishedModule : ", finishedModule);
        var concurentXhrLimit = Archetype.concurentXhrLimit;
        var callbacks = $A();
        if(modules !== null) {
            var addition = $A();
            modules.each(function(module, i) {
                modules[i] = $U(module, options.evaluation);
                addition.push(modules[i]);
                addition.push(options.evaluation);
            //addition.push(options.storing);
            });
            //Priorize the component loading in the queue,as they have their own dependencies
            if(options.evaluation == "component") {
                //prepend it in the queue
                Archetype.loaderQueue.queue = addition.concat(Archetype.loaderQueue.queue);
            } else {
                //postpend it in the queue
                Archetype.loaderQueue.queue = Archetype.loaderQueue.queue.concat(addition);
            }

            Archetype.loaderQueue.callbacks.push(modules);
            Archetype.loaderQueue.callbacks.push(options.callback);
            Archetype.loaderQueue.callbacks.push({}); //callback data

        //Logger.log("LOADER add modules " + modules);
        }
        if(finishedModule !== null) {
            Archetype.loaderQueue.currents = Archetype.loaderQueue.currents.without(finishedModule);
            for(var i = 0; i < Archetype.loaderQueue.callbacks.length; i += 3) {
                if(Archetype.loaderQueue.callbacks[i].include(finishedModule)) {
                    Object.extend(Archetype.loaderQueue.callbacks[i + 2], callbackData);
                }
                Archetype.loaderQueue.callbacks[i] = Archetype.loaderQueue.callbacks[i].without(finishedModule);
                if(Archetype.loaderQueue.callbacks[i].length === 0) {
                    //if(finishedModule.indexOf(".html") != -1) {
                    //	Logger.warn("callback", Archetype.loaderQueue.callbacks[i + 2]);
                    //	Archetype.loaderQueue.callbacks[i + 1]("toto", "toto","toto","toto");
                    //}
                    callbacks.push(Archetype.loaderQueue.callbacks[i + 1].bind(this, Archetype.loaderQueue.callbacks[i + 2]));
                    Archetype.loaderQueue.callbacks.splice(i, 3);
                    i -= 3;
                }
            }
        }
        while(Archetype.loaderQueue.queue.length > 0 && Archetype.loaderQueue.currents.length < concurentXhrLimit) {
            var module = Archetype.loaderQueue.queue.shift();
            //Logger.log("LOADER require " + module + " queue " + Archetype.loaderQueue.queue);
            var evaluation = Archetype.loaderQueue.queue.shift();
            //var storing = Archetype.loaderQueue.queue.shift();
            //Logger.warn(Archetype.loaderQueue.currents.inspect() + " -- " + module + " -- " + Archetype.loaderQueue.currents.member(module));

            //Copy of the Protoype's include core method to be sure using a simple equality
            var found = false;
            Archetype.loaderQueue.currents.each(function(current) {
                if (current == module) {
                    found = true;
                    throw $break;
                }
            });
            
            if(!Archetype.isLoadedModule(module) && !found) {
                Archetype.loaderQueue.currents.push(module);
                this._singleLoad(module, {
                    asynchronous: options != null ? options.asynchronous : true,
                    evaluation: evaluation,
                    callback: this._loader.bind(this, null, null, module)//,
                //storing: storing
                });
            }
        }

        $A(callbacks).invoke("call");
    },

    /**
     * /!\Can't be used before the bootstrap.
     * Launch either XHR or adding script on link tag for loading for the module in parameter
     * @param module
     * @param options
     * @private
     */
    _singleLoad: function(/*String*/ module, /*Hashmap*/ options) {
        var url = $U(module, options.evaluation);
    	
        //Handle evaluation modification when configuration overide file extension
        var evaluation = options.evaluation;
        $A(["js", "html", "css"]).each(function(type) {
            $A(Archetype.extensions[type]).each(function(extension) {
                var regexp = new RegExp("\." + extension + "$");
                if(regexp.test(url)) {
                    evaluation = type;
                }
            });
        });
    	
        var callback;
        //Logger.warn("LOAD : ", url, ", ", evaluation, ", ", options.asynchronous);
        if(evaluation == true || evaluation == "js" || evaluation == "component") {
            Archetype.addJS(url, Archetype._onRequireSuccess.bind(this, module, options));
        } else if(evaluation == "css") {
            Archetype.addCSS(url);
            Archetype._onRequireSuccess(module, options);
        } else { //evaluation == "html"
            Logger.debug("XHR : " , url);
            Archetype.engine.ajax(url, options.asynchronous,
                function() {
                    try {
                        Archetype._onRequireSuccess.bind(this, module, options).apply(this, arguments);
                    } catch (exception) {
                        Logger.error("Callback of XHR on " + module + " failed with exception ", exception);
                    }
                },
                function(transport) {
                    Logger.warn("XML HTTP REQUEST Exception : ", url, transport);
                }
                );
        }
        return this.returnContent;
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Used after all require as end callback function.
     * @param {String} module
     * @param {Hashmap} options
     * @param {Object} transport : Ajax request transport object.
     * @private
     */
    _onRequireSuccess: function(/*String*/ module, /*Hashmap*/ options, /*Object*/ responseText) {
        this.returnContent = responseText;
        Archetype._provide(module);
        if(options.callback) {
            var data = {};
            data[module] = responseText;
            options.callback(data);
        }
    }
};

/**
 * TODO: Modularize i18n and complete i18n management
 * @param {Object} templateString
 */
Archetype.i18n = function(templateString) {
    return templateString.replace(/{lang}/g, (navigator.userLanguage || navigator.language));
};

/**
 * @static $U
 * @see Archetype.getUrl
 */
var $U = Archetype.getUrl;
/**
  * @static $$U
  * @see Archetype.getModuleFromUrl
  */
var $$U = Archetype.getModuleFromUrl;
/**
 * @static $N
 * @see Archetype.absoluteNamespace
 */
var $N = Archetype.absoluteNamespace;
/**
 * @static $$N
 * @see Archetype.relativeNamespace
 */
var $$N = Archetype.relativeNamespace;
/**
 * @static $exist
 * checks if an element is null or undefined
 */
var $exist = function(element) {
    return ! (typeof element == "undefined" || element == null);
};

Archetype.load();
